{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Segmentation\n",
    "\n",
    "Image segmentation is another early as well as an important image processing task. Segmentation is the process of breaking an image into groups, based on similarities of the pixels. Pixels can be similar to each other in multiple ways like brightness, color, or texture. The segmentation algorithms are to ﬁnd a partition of the image into sets of similar pixels which usually indicating objects or certain scenes in an image.\n",
    "\n",
    "The segmentations in this chapter can be categorized into two complementary ways: one focussing on detecting the boundaries of these groups, and the other on detecting the groups themselves, typically called regions. We will introduce some principles of some algorithms in this notebook to present the basic ideas in segmentation."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Probability Boundary Detection\n",
    "\n",
    "A boundary curve passing through a pixel $(x,y)$ in an image will have an orientation $\\theta$, so we can formulize boundary detection problem as a classification problem. Based on features from a local neighborhood, we want to compute the probability $P_b(x,y,\\theta)$ that indeed there is a boundary curve at that pixel along that orientation. \n",
    "\n",
    "One of the sampling ways to calculate $P_b(x,y,\\theta)$ is to generate a series sub-divided into two half disks by a diameter oriented at θ. If there is a boundary at (x, y, θ) the two half disks might be expected to differ signiﬁcantly in their brightness, color, and texture. For detailed proof of this algorithm, please refer to this [article](https://people.eecs.berkeley.edu/~malik/papers/MFM-boundaries.pdf)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Implementation\n",
    "\n",
    "We implemented a simple demonstration of probability boundary detector as `probability_contour_detection` in `perception.py`. This method takes three inputs:\n",
    "\n",
    "- image: an image already transformed into the type of numpy ndarray.\n",
    "- discs: a list of sub-divided discs.\n",
    "- threshold: the standard to tell whether the difference between intensities of two discs implying there is a boundary passing the current pixel.\n",
    "\n",
    "we also provide a helper function `gen_discs` to gen a list of discs. It takes `scales` as the number of sizes of discs will be generated which is default 1. Please note that for each scale size, there will be 8 sub discs generated which are in the horizontal, verticle and two diagnose directions. Another `init_scale` indicates the starting scale size. For instance, if we use `init_scale` of 10 and `scales` of 2, then scales of sizes of 10 and 20 will be generated and thus we will have 16 sub-divided scales."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Example\n",
    "\n",
    "Now let's demonstrate the inner mechanism with our navie implementation of the algorithm. First, let's generate some very simple test images. We already generated a grayscale image with only three steps of gray scales in `perceptron.py`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Using TensorFlow backend.\n"
     ]
    }
   ],
   "source": [
    "import os, sys\n",
    "sys.path = [os.path.abspath(\"../../\")] + sys.path\n",
    "from perception4e import *\n",
    "from notebook4e import *\n",
    "import matplotlib.pyplot as plt"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's take a look at it:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAOcAAADnCAYAAADl9EEgAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjEsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy8QZhcZAAAC7UlEQVR4nO3YMYrEMBAAwdViPVsvd6DLD68vOdYdVIWaZDA0Ax577xfQ8356AeCaOCFKnBAlTogSJ0Qdd8Mxhl+5N9ZaT6+Q5xv9bc45rt5dTogSJ0SJE6LECVHihChxQpQ4IUqcECVOiBInRIkTosQJUeKEKHFClDghSpwQJU6IEidEiROixAlR4oQocUKUOCFKnBAlTogSJ0SJE6LECVHihChxQpQ4IUqcECVOiBInRIkTosQJUeKEKHFClDghSpwQJU6IEidEiROixAlR4oQocUKUOCFKnBAlTogSJ0SJE6LECVHihChxQpQ4IUqcECVOiBInRIkTosQJUeKEKHFClDghSpwQJU6IEidEiROixAlR4oQocUKUOCFKnBB13A3XWt/aA/jF5YQocUKUOCFKnBAlTogSJ0SJE6LECVHihChxQpQ4IUqcECVOiBInRIkTosQJUeKEKHFClDghSpwQJU6IEidEiROixAlR4oQocUKUOCFKnBAlTogSJ0SJE6LECVHihChxQpQ4IUqcECVOiBInRIkTosQJUeKEKHFClDghSpwQJU6IEidEiROixAlR4oQocUKUOCFKnBAlTogSJ0SJE6LECVHihChxQpQ4IUqcECVOiBInRIkTosQJUeKEKHFClDghSpwQJU6IEidEiROixt774/A8z89D4F/MOcfVu8sJUeKEKHFClDghSpwQJU6IEidEiROixAlR4oQocUKUOCFKnBAlTogSJ0SJE6LECVHihChxQpQ4IUqcECVOiBInRIkTosQJUeKEKHFClDghSpwQJU6IEidEiROixAlR4oQocUKUOCFKnBAlTogSJ0SJE6LECVHihChxQpQ4IUqcECVOiBInRIkTosQJUeKEKHFClDghSpwQJU6IEidEiROixAlR4oQocUKUOCFKnBAlTogSJ0SJE6LECVHihChxQpQ4IUqcECVOiBInRIkTosbe++kdgAsuJ0SJE6LECVHihChxQpQ4IeoHudIUPgvLLmwAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.imshow(gray_scale_image, cmap='gray', vmin=0, vmax=255)\n",
    "plt.axis('off')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can also generate your own grayscale images by calling `gen_gray_scale_picture` and pass the image size and grayscale levels needed:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAOcAAADnCAYAAADl9EEgAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjEsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy8QZhcZAAADB0lEQVR4nO3YMYrEMBAAQesw+Ff6/5c20uWL11xweNtQFWqSSZoBjbXWBvT8fHsB4Jw4IUqcECVOiBInRO1XwzHGo75y55zfXuHPnrTrtj1r3yftum3bdhzHOHt3OSFKnBAlTogSJ0SJE6LECVHihChxQpQ4IUqcECVOiBInRIkTosQJUeKEKHFClDghSpwQJU6IEidEiROixAlR4oQocUKUOCFKnBAlTogSJ0SJE6LECVHihChxQpQ4IUqcECVOiBInRIkTosQJUeKEKHFClDghSpwQJU6I2q+Gc8679gDeuJwQJU6IEidEiROixAlR4oQocUKUOCFKnBAlTogSJ0SJE6LECVHihChxQpQ4IUqcECVOiBInRIkTosQJUeKEKHFClDghSpwQJU6IEidEiROixAlR4oQocUKUOCFKnBAlTogSJ0SJE6LECVHihChxQpQ4IUqcECVOiNqvhnPOu/YA3ricECVOiBInRIkTosQJUeKEKHFClDghSpwQJU6IEidEiROixAlR4oQocUKUOCFKnBAlTogSJ0SJE6LECVHihChxQpQ4IUqcECVOiBInRIkTosQJUeKEKHFClDghSpwQJU6IEidEiROixAlR4oQocUKUOCFKnBAlTogSJ0TtV8M55117AG9cTogSJ0SJE6LECVHihChxQpQ4IUqcECVOiBInRIkTosQJUeKEKHFClDghSpwQJU6IEidEiROixAlR4oQocUKUOCFKnBAlTogSJ0SJE6LECVHihChxQpQ4IUqcECVOiBInRIkTosQJUeKEKHFClDghSpwQJU6IEidEjbXWx+Hr9fo8BP7FcRzj7N3lhChxQpQ4IUqcECVOiBInRIkTosQJUeKEKHFClDghSpwQJU6IEidEiROixAlR4oQocUKUOCFKnBAlTogSJ0SJE6LECVHihChxQpQ4IUqcECVOiBInRIkTosQJUeKEKHFClDghSpwQJU6IEidEiROixAlR4oQocUKUOCFqrLW+vQNwwuWEKHFClDghSpwQJU6IEidE/QKgERT5nIvuXQAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "gray_img = gen_gray_scale_picture(100, 5)\n",
    "plt.imshow(gray_img, cmap='gray', vmin=0, vmax=255)\n",
    "plt.axis('off')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now let's generate the discs we are going to use as sampling masks to tell the intensity difference between two half of the care area of an image. We can generate the discs of size 100 pixels and show them:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 720x720 with 8 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "discs = gen_discs(100, 1)\n",
    "fig=plt.figure(figsize=(10, 10))\n",
    "for i in range(8):\n",
    "    img = discs[0][i]\n",
    "    fig.add_subplot(1, 8, i+1)\n",
    "    plt.axis('off')\n",
    "    plt.imshow(img, cmap='gray', vmin=0, vmax=255)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The white part of disc images is of value 1 while dark places are of value 0. Thus convolving the half-disc image with the corresponding area of an image will yield only half of its content. Of course, discs of size 100 is too large for an image of the same size. We will use discs of size 10 and pass them to the detector."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [],
   "source": [
    "discs = gen_discs(10, 1)\n",
    "contours = probability_contour_detection(gray_img, discs[0])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAOcAAADnCAYAAADl9EEgAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjEsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy8QZhcZAAADOElEQVR4nO3dwUrDUBRF0V7x/3/5ORaKKL2SHV1rqBDewN1TJG3mnPMAet6uPgDwnDghSpwQJU6IEidEvX/1y5nxr1z4ZeecefZzywlR4oQocUKUOCFKnBAlTogSJ0SJE6LECVHihKgvb98reuXD4TNP75L6VXc676sfvL/Tea/4W/gpywlR4oQocUKUOCFKnBAlTogSJ0SJE6LECVHihChxQpQ4IUqcECVOiBInRIkTosQJUeKEKHFClDghSpwQJU6IEidEiROixAlRa9/4/uq3hQOfWU6IEidEiROixAlR4oQocUKUOCFKnBAlTogSJ0SJE6LECVFrN76XzczVR4Afs5wQtbac1gl2WU6IEidEiROixAlR4oQocUKUOCFKnBAlTogSJ0SJE6LECVHihChxQpQ4IUqcECVOiBInRIkTosQJUeKEKHFClDghSpwQJU6IEidEiROixAlRaw8yOudsXQp4WE7IEidEiROixAlR4oQocUKUOCFKnBAlTogSJ0SJE6LECVFrN76zZ2auPgIBlhOi1pbTqz3sspwQJU6IEidEiROixAlR4oQocUKUOCFKnBAlTogSJ0SJE6LECVHihChxQpQ4IUqcECVOiBInRIkTosQJUeKEKHFClDghSpwQJU6IEidErT0r5ZyzdSngYTkhS5wQJU6IEidEiROixAlR4oQocUKUOCFKnBAlTogSJ0St3fjO/zQzVx/hz7KcECVOiFp7W+vtDeyynBAlTogSJ0SJE6LECVHihChxQpQ4IUqcECVOiBInRIkTosQJUeKEKHFClDghSpwQJU6IEidEiROixAlR4oQocUKUOCFKnBAlTogSJ0StPSvlnLN1KeBhOSFLnBAlTogSJ0SJE6LECVHihChxQpQ4IUqcECVOiBInRK3d+A53MDNXH+HbLCdErS3nnV6R4A4sJ0SJE6LECVHihChxQpQ4IUqcECVOiBInRIkTosQJUeKEKHFClDghSpwQJU6IEidEiROixAlR4oQocUKUOCFKnBAlTogSJ0SJE6LECVHihChxQpQ4IUqcECVOiBInRIkTouacc/UZgCcsJ0SJE6LECVHihChxQpQ4IeoDHL0j76PZjhkAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "show_edges(contours)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As we are using discs of size 10 and some boundary conditions are not dealt with in our naive algorithm, the extracted contour has a bold edge with missings near the image border. But the main structures of contours are extracted correctly which shows the ability of this algorithm."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Group Contour Detection\n",
    "\n",
    "The alternative approach is based on trying to “cluster” the pixels into regions based on their brightness, color and texture properties. There are multiple grouping algorithms and the simplest and the most popular one is k-means clustering. Basically, the k-means algorithm starts with k randomly selected centroids, which are used as the beginning points for every cluster, and then performs iterative calculations to optimize the positions of the centroids. For a detailed description, please refer to the chapter of unsupervised learning."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Implementation\n",
    "\n",
    "Here we will use the module of `cv2` to perform K-means clustering and show the image. To use it you need to have `opencv-python` pre-installed. Using `cv2.kmeans` is quite simple, you only need to specify the input image and the characters of cluster initialization. Here we use modules provide by `cv2` to initialize the clusters. `cv2.KMEANS_RANDOM_CENTERS` can randomly generate centers of clusters and the cluster number is defined by the user.\n",
    "\n",
    "`kmeans` method will return the centers and labels of clusters, which can be used to classify pixels of an image. Let's try this algorithm again on the small grayscale image we imported:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {},
   "outputs": [],
   "source": [
    "contours = group_contour_detection(gray_scale_image, 3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now let's show the extracted contours:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAOcAAADnCAYAAADl9EEgAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjEsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy8QZhcZAAAC7UlEQVR4nO3YMYrEMBAAwdViPVsvd6DLD68vOdYdVIWaZDA0Ax577xfQ8356AeCaOCFKnBAlTogSJ0Qdd8Mxhl+5N9ZaT6+Q5xv9bc45rt5dTogSJ0SJE6LECVHihChxQpQ4IUqcECVOiBInRIkTosQJUeKEKHFClDghSpwQJU6IEidEiROixAlR4oQocUKUOCFKnBAlTogSJ0SJE6LECVHihChxQpQ4IUqcECVOiBInRIkTosQJUeKEKHFClDghSpwQJU6IEidEiROixAlR4oQocUKUOCFKnBAlTogSJ0SJE6LECVHihChxQpQ4IUqcECVOiBInRIkTosQJUeKEKHFClDghSpwQJU6IEidEiROixAlR4oQocUKUOCFKnBB13A3XWt/aA/jF5YQocUKUOCFKnBAlTogSJ0SJE6LECVHihChxQpQ4IUqcECVOiBInRIkTosQJUeKEKHFClDghSpwQJU6IEidEiROixAlR4oQocUKUOCFKnBAlTogSJ0SJE6LECVHihChxQpQ4IUqcECVOiBInRIkTosQJUeKEKHFClDghSpwQJU6IEidEiROixAlR4oQocUKUOCFKnBAlTogSJ0SJE6LECVHihChxQpQ4IUqcECVOiBInRIkTosQJUeKEKHFClDghSpwQJU6IEidEiROixt774/A8z89D4F/MOcfVu8sJUeKEKHFClDghSpwQJU6IEidEiROixAlR4oQocUKUOCFKnBAlTogSJ0SJE6LECVHihChxQpQ4IUqcECVOiBInRIkTosQJUeKEKHFClDghSpwQJU6IEidEiROixAlR4oQocUKUOCFKnBAlTogSJ0SJE6LECVHihChxQpQ4IUqcECVOiBInRIkTosQJUeKEKHFClDghSpwQJU6IEidEiROixAlR4oQocUKUOCFKnBAlTogSJ0SJE6LECVHihChxQpQ4IUqcECVOiBInRIkTosbe++kdgAsuJ0SJE6LECVHihChxQpQ4IeoHudIUPgvLLmwAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "show_edges(contours)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It is not obvious as our generated image already has very clear boundaries. Let's apply the algorithm on the stapler example to see whether it will be more obvious:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.image as mpimg\n",
    "\n",
    "stapler_img = mpimg.imread('images/stapler.png', format=\"gray\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.image.AxesImage at 0x141c36da0>"
      ]
     },
     "execution_count": 50,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "contours = group_contour_detection(stapler_img, 5)\n",
    "plt.axis('off')\n",
    "plt.imshow(contours, cmap=\"gray\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The segmentation is very rough when using only 5 clusters. Adding to the cluster number will increase the degree of subtle of each group thus the whole picture will be more alike the original one:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.image.AxesImage at 0x142c2a780>"
      ]
     },
     "execution_count": 51,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "contours = group_contour_detection(stapler_img, 15)\n",
    "plt.axis('off')\n",
    "plt.imshow(contours, cmap=\"gray\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Minimum Cut Segmentation\n",
    "\n",
    "Another way to do clustering is by applying the minimum cut algorithm in graph theory. Roughly speaking, the criterion for partitioning the graph is to minimize the sum of weights of connections across the groups and maximize the sum of weights of connections within the groups.\n",
    "\n",
    "### Implementation\n",
    "\n",
    "There are several kinds of representations of a graph such as a matrix or an adjacent list. Here we are using a util function `image_to_graph` to convert an image in ndarray type to an adjacent list. It is integrated into the class of `Graph`. `Graph` takes an image as input and offer the following implementations of some graph theory algorithms:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- bfs: performing bread searches from a source vertex to a terminal vertex. Return `True` if there is a path between the two nodes else return `False`.\n",
    "\n",
    "- min_cut: performing minimum cut on a graph from a source vertex to sink vertex. The method will return the edges to be cut.\n",
    "\n",
    "Now let's try the minimum cut method on a simple generated grayscale image of size 10:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 67,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAOcAAADnCAYAAADl9EEgAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjEsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy8QZhcZAAAC3ElEQVR4nO3YMQrDMBAAwSjY//+vGqUPwpAmXsNMqWuuWQ401lovoOd99wLAnjghSpwQJU6IEidEHVfDMYav3IeZc969Aj86z3Ps3l1OiBInRIkTosQJUeKEKHFClDghSpwQJU6IEidEiROixAlR4oQocUKUOCFKnBAlTogSJ0SJE6LECVHihChxQpQ4IUqcECVOiBInRIkTosQJUeKEKHFClDghSpwQJU6IEidEiROixAlR4oQocUKUOCFKnBAlTogSJ0SJE6LECVHihChxQpQ4IUqcECVOiBInRIkTosQJUeKEKHFClDghSpwQJU6IEidEiROixAlR4oQocUKUOCFKnBAlTogSJ0SJE6LECVHihChxQpQ4IUqcECVOiBInRIkTosQJUeKEKHFClDghSpwQJU6IEidEiROixAlR4oQocUKUOCFKnBAlTogSJ0SJE6LECVHihChxQpQ4IUqcECVOiBInRIkTosQJUeKEKHFClDghSpwQJU6IEidEiROixAlRx9VwzvmvPYAvLidEiROixAlR4oQocUKUOCFKnBAlTogSJ0SJE6LECVHihChxQpQ4IUqcECVOiBInRIkTosQJUeKEKHFClDghSpwQJU6IEidEiROixAlR4oQocUKUOCFKnBAlTogSJ0SJE6LECVHihChxQpQ4IUqcECVOiBInRIkTosQJUeKEKHFClDghSpwQJU6IEidEiROixAlR4oQocUKUOCFKnBAlTogSJ0SJE6LECVHihChxQpQ4IUqcECVOiBInRIkTosQJUeKEKHFClDghSpwQJU6IEidEiROixAlR4oQocUKUOCFKnBAlTogSJ0SJE6LECVHihChxQpQ4IUqcECVOiBInRIkTosQJUeKEKHFClDghSpwQJU6IEidEiROixAlR4oQocUKUOCFKnBAlTogSJ0SJE6LECVHihKix1rp7B2DD5YQocUKUOCFKnBAlTogSJ0R9AF+CDrluZqs6AAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "image = gen_gray_scale_picture(size=10, level=2)\n",
    "show_edges(image)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 66,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[((0, 4), (0, 5)),\n",
       " ((1, 4), (1, 5)),\n",
       " ((2, 4), (2, 5)),\n",
       " ((3, 4), (3, 5)),\n",
       " ((4, 0), (5, 0)),\n",
       " ((4, 1), (5, 1)),\n",
       " ((4, 2), (5, 2)),\n",
       " ((4, 3), (5, 3)),\n",
       " ((4, 4), (5, 4)),\n",
       " ((4, 4), (4, 5))]"
      ]
     },
     "execution_count": 66,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "graph = Graph(image)\n",
    "graph.min_cut((0,0), (9,9))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "There are ten edges to be cut. By cutting the ten edges, we can separate the pictures into two parts by the pixel intensities."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
